package its_models.expert_model.expert_help_util;

import java.util.ArrayList;
import sudoku_properties.BasicSudokuParam ;

/************************************************************
 * Class StrategyGraphUnit
 * 1. Store the coordinate of cells
 * 2. Store the possible value of cells
 * 3. Store the information of method to solve the cell
 ************************************************************/
public class StrategyGraphUnit {

    /* Constructors */
    public StrategyGraphUnit(){

        this.cellCoordinate = new SudokuCoordinate() ;
        this.possibilityValues = new ArrayList<Integer> () ;

        /* default all value is possible */
        for( int i = MIN_CELL_VALUE ; i <= NUM_CELLS_IN_UNIT ; i++){
            this.setPossValue(i) ;
        }

        this.isDefiniteValueSet = false;
        this.methodApplied  = UNKNOWN ;
        this.methodEffectiveIn = UNKNOWN ;
        this.purposeOfLastMethod = UNKNOWN ;
    }
    public StrategyGraphUnit( int inputCellIndexX, int inputCellIndexY ){
        final int cellIndexX = inputCellIndexX;
        final int cellIndexY = inputCellIndexY;

        this.cellCoordinate = new SudokuCoordinate(cellIndexX, cellIndexY) ;
        this.possibilityValues = new ArrayList<Integer> () ;

        /* default all value is possible */
        for( int i = MIN_CELL_VALUE ; i <= NUM_CELLS_IN_UNIT ; i++){
            this.setPossValue(i) ;
        }
        this.isDefiniteValueSet = false;
        this.methodApplied  = UNKNOWN ;
        this.methodEffectiveIn = UNKNOWN ;
        this.purposeOfLastMethod = UNKNOWN ;
    }
    
    public void copy( StrategyGraphUnit copyPossibilityUnit, int cellIndexX, int cellIndexY ) {

        this.cellCoordinate = new SudokuCoordinate(cellIndexX, cellIndexY);
        //this.possibilityValues = copyPossibilityUnit.getpossibilityValues();
        this.possibilityValues = new ArrayList<Integer> () ;
        for( int i=0 ; i < copyPossibilityUnit.getNumOfPossValues() ; i++ ){
             this.possibilityValues.add(copyPossibilityUnit.getPossValueAt(i)) ;
        }
        
        //for( int i=0 ; i < copyPossibilityUnit.getNumOfPossValues() ; i++ ){
          //   this.setPossValue(i) ;
          //  this.possibilityValues.add(copyPossibilityUnit.getPossValueAt(i)) ;
        //}
        this.isDefiniteValueSet = copyPossibilityUnit.getDefiniteValueSet();
        this.methodApplied = copyPossibilityUnit.getLogicalRule();
        this.methodEffectiveIn = copyPossibilityUnit.getScopeEffectiveIn() ;
        this.purposeOfLastMethod = copyPossibilityUnit.getPurposeOfLastLogicalRule() ;
    }
     public void copy( StrategyGraphUnit copyPossibilityUnit) {

        this.cellCoordinate = copyPossibilityUnit.getCellCoordinate();
        this.possibilityValues = new ArrayList<Integer> () ;

        //this.possibilityValues = copyPossibilityUnit.getpossibilityValues();
        for( int i=0 ; i < copyPossibilityUnit.getNumOfPossValues() ; i++ ){
             //this.setPossValue(i) ;
             this.possibilityValues.add(copyPossibilityUnit.getPossValueAt(i)) ;
        }
        this.isDefiniteValueSet = copyPossibilityUnit.getDefiniteValueSet();
        this.methodApplied = copyPossibilityUnit.getLogicalRule();
        this.methodEffectiveIn = copyPossibilityUnit.getScopeEffectiveIn() ;
        this.purposeOfLastMethod = copyPossibilityUnit.getPurposeOfLastLogicalRule() ;
    }

    public ArrayList<Integer> getpossibilityValues(){
        return this.possibilityValues;
    }

    public void reset(){

        this.possibilityValues.clear();
        /* default all value is possible */
        for( int i = MIN_CELL_VALUE ; i <= NUM_CELLS_IN_UNIT ; i++){
            this.setPossValue(i) ;
        }

        this.isDefiniteValueSet = false;
        this.methodApplied  = UNKNOWN ;
        this.methodEffectiveIn = UNKNOWN ;
        this.purposeOfLastMethod = UNKNOWN ;
    }

    /* Method to set a Definite Value to the cell by Logical Rule. */
    public void setDefiniteValue( int inputDefiniteValue, String logicalRule, String effectiveScope, String purposeOfApplied ){
        final int definiteValue = inputDefiniteValue;
        possibilityValues.clear() ;
        possibilityValues.add(definiteValue) ;
        // 20091029 phrase2 start
        //methodApplied  = logicalRule ;
        // 200911029 phrase2 end 
        methodEffectiveIn = effectiveScope ;
        purposeOfLastMethod = purposeOfApplied ;
        isDefiniteValueSet = true;
    }

    public void setDefiniteValue( int inputDefiniteValue){
        final int definiteValue = inputDefiniteValue;
        possibilityValues.clear() ;
        possibilityValues.add(definiteValue) ;
        isDefiniteValueSet = true;
    }

    public void removeDefiniteValue(){
        isDefiniteValueSet = false;
    }

    public boolean getDefiniteValueSet(){
        return isDefiniteValueSet;
    }
    /* set cell value poss or imposs */
    public boolean setPossValue(int inputPossValue){

        boolean isPossValueSet = false ;
        final int possValue = inputPossValue;
        if( possibilityValues.indexOf(possValue) == UNKNOWN_FLAG){

            final int NUM_OF_POSS_VALUE = possibilityValues.size() ;
            /* Order the arraylist */
            for( int i = 0 ; i < NUM_OF_POSS_VALUE ; ++i ) {
                if( (!isPossValueSet) && (possValue < possibilityValues.get( i )) ){
                    possibilityValues.add( i, possValue) ;
                    isPossValueSet = true ;
                    break;
                }
            }
            /* add value to the back of the arraylist */
            if(!isPossValueSet) {
                possibilityValues.add( possValue) ;
                isPossValueSet = true ;
            }
        }
        return isPossValueSet ;
    }

    public boolean setImpossValue( int imputImpossValue )
    {
        boolean isImPossValueSet = false ;
        final int inpossValue = imputImpossValue;
        final int IMPOSS_VALUE_INDEX = possibilityValues.indexOf(inpossValue) ;
        final int NUM_OF_POSS_VALUE = possibilityValues.size() ;

        if((NUM_OF_POSS_VALUE > 1) && (-1 != IMPOSS_VALUE_INDEX)){
            possibilityValues.remove( IMPOSS_VALUE_INDEX) ;
            isImPossValueSet = true ;
        }
        return isImPossValueSet ;
    }

    /* set method information */
    public void setLogicalRule(String logicalRule)
    {
        methodApplied = logicalRule ;
    }
    public void setScopeEffectiveIn( String effectiveIn )
    {
        methodEffectiveIn = effectiveIn ;
    }
    public void setPurposeOfLastLogicalRule( String purposeOfApplied )
    {
        purposeOfLastMethod = purposeOfApplied ;
    }

    /* get infomation */
    public SudokuCoordinate getCellCoordinate(){
        return cellCoordinate ;
    }
    public ArrayList<Integer> getListOfPossibleValues(){
        return possibilityValues ;
    }
     public int getDefiniteValue(){
        int definiteValue = UNKNOWN_FLAG ;
        if( this.getNumOfPossValues() == 1){
            definiteValue = getPossValueAt(0) ;
        }
        return definiteValue ;
    }
    public String getLogicalRule(){
        return methodApplied ;
    }
    public String getScopeEffectiveIn() {
        return methodEffectiveIn ;
    }
    public String getPurposeOfLastLogicalRule(){
        return purposeOfLastMethod ;
    }
    public int getNumOfPossValues(){
        return possibilityValues.size() ;
    }
    public int getPossValueAt(int inputIndex){
        final int index = inputIndex;
        final int NUM_OF_POSS_VALUE = possibilityValues.size() ;
        int possValue = UNKNOWN_FLAG ;

        if( index < NUM_OF_POSS_VALUE ){
            possValue = possibilityValues.get(index) ;
        }
        return possValue ;
    }

    /* Support for SudokuCoordinate */
    public int getColumnIndex(){
        return cellCoordinate.getColumnIndex() ;
    }

    public int getRowIndex(){
        return cellCoordinate.getRowIndex() ;
    }
    public int getBoxIndex(){
        return cellCoordinate.getBoxIndex() ;
    }
    public int[][] getSameColumnIndices(){
        return cellCoordinate.getSameColumnCoordinates() ;
    }
    public int[][] getSameRowIndices(){
        return cellCoordinate.getSameRowCoordinates() ;
    }
    public int[][] getSameBoxIndices(){
        return cellCoordinate.getSameBoxCoordinates() ;
    }

    /* support cellValuePossibilityUnit calculation */
    /* get value in one cell but not hte other */
    public int[] getPossValuesNotIn(StrategyGraphUnit strategyGraphUnit){
        int[] subtractValues = null;

        final int THIS_NUM_OF_POSS_VALUE = this.getNumOfPossValues() ;
        final int THAT_NUM_OF_POSS_VALUE = strategyGraphUnit.getNumOfPossValues() ;
        final int NUM_OF_SUBTRACTED_VALUE = THIS_NUM_OF_POSS_VALUE - THAT_NUM_OF_POSS_VALUE ;

        if( NUM_OF_SUBTRACTED_VALUE > 0 ){
            subtractValues = new int[NUM_OF_SUBTRACTED_VALUE] ;
            int notInElementCounter = 0 ;
            for( int i= 0 ; i<THIS_NUM_OF_POSS_VALUE ; i++){
                final int POSS_VALUE_TO_CHECK = this.possibilityValues.get(i) ;
                if( !strategyGraphUnit.isPossibleFor(POSS_VALUE_TO_CHECK) ){
                    subtractValues[notInElementCounter] = POSS_VALUE_TO_CHECK ;
                    ++notInElementCounter;
                }
            }
        }
        return subtractValues ;
    }


    public boolean hasDefiniteValue(){
        final int NUM_OF_POSS_VALUE = possibilityValues.size() ;
        return (NUM_OF_POSS_VALUE == 1) ;
    }


    public boolean isUnknowLogicalRule(){
        return ( methodApplied.toUpperCase() ).equals( UNKNOWN.toUpperCase()) ;
    }
    public boolean isPossibleFor( int inputValue){
        return ( possibilityValues.indexOf(inputValue)!= UNKNOWN_FLAG ) ;
    }
    public boolean equalCoordinate(StrategyGraphUnit inputCellValuePossibilityUnit){
        final StrategyGraphUnit cellValuePossibilityUnit = inputCellValuePossibilityUnit;
        return (this.getCellCoordinate().equal(cellValuePossibilityUnit.getCellCoordinate()) ) ;
    }
    public boolean equalmethodApplied(StrategyGraphUnit inputCellValuePossibilityUnit){
        final StrategyGraphUnit cellValuePossibilityUnit = inputCellValuePossibilityUnit;
        return ( this.methodApplied.equals(cellValuePossibilityUnit.getLogicalRule()) ) ;
    }
    public boolean equalNumOfPoss(StrategyGraphUnit inputCellValuePossibilityUnit){
        final StrategyGraphUnit cellValuePossibilityUnit = inputCellValuePossibilityUnit;
        return ( this.getNumOfPossValues() == cellValuePossibilityUnit.getNumOfPossValues() ) ;
    }

    /* Method to check if this two CellValuePossibilityUnit has the same Possibilities of Values */
    public boolean equalPossibilities(StrategyGraphUnit inputCellValuePossibilityUnit){
        boolean isSamePossibilities = true;
        final StrategyGraphUnit cellValuePossibilityUnit = inputCellValuePossibilityUnit;

        for( int i=0 ; i< this.getNumOfPossValues() ;i++){
            isSamePossibilities = (this.getPossValueAt(i) == cellValuePossibilityUnit.getPossValueAt(i)) ;
            if(!isSamePossibilities){
                break;
            }
        }
        return isSamePossibilities ;
    }

    public boolean sameScope(StrategyGraphUnit inputCellValuePossibilityUnit){
        final StrategyGraphUnit cellValuePossibilityUnit = inputCellValuePossibilityUnit;
        return ( this.methodEffectiveIn.equals(cellValuePossibilityUnit.getScopeEffectiveIn() ) ) ;
    }

    public boolean equalUnits( StrategyGraphUnit unitToCheck ){
        boolean isEqual = false;
        isEqual = ( this.equalCoordinate(unitToCheck) )
                && ( this.equalmethodApplied(unitToCheck) )
                && ( this.equalNumOfPoss(unitToCheck) )
                && ( this.sameScope(unitToCheck) )
                && ( this.equalPossibilities(unitToCheck) ) ;

        return isEqual ;
    }

    @Override
    public String toString(){
        String str = "Coordinate " + cellCoordinate.getRowIndex() + "," +  cellCoordinate.getColumnIndex()
                + " with definite value " + this.getDefiniteValue() + " using the rules - " + this.getLogicalRule()
                + " which effective in " + this.getScopeEffectiveIn();
        return str;
    }

    /* local variables */
    /* the coordinate of this cell */
    private SudokuCoordinate cellCoordinate ;
    /* the possible value within this cell, values are arrange is order*/
    private ArrayList<Integer> possibilityValues ;
    /* the definite value is set */
    private boolean isDefiniteValueSet;
    /* the method to recover the cell value */
    private String methodApplied ;
    /* scope that the scope that the applied method effective in */
    private String methodEffectiveIn ;
    /* the purpose of the last rule applied on this cell */
    private String purposeOfLastMethod ;


    /* Constant variables */
    /*BasicSudokuParam*/
    final static int NUM_CELLS_IN_UNIT = BasicSudokuParam.getNumCellsInUnit();
    final static int MIN_CELL_VALUE = BasicSudokuParam.getMinCellValue();
    final static int MAX_CELL_VALUE = BasicSudokuParam.getMaxCellValue();

    final static String UNKNOWN = "UN" ;
    final static int UNKNOWN_FLAG = -1 ;
}
